package de.rj.urwalking.util;

import java.io.File;
import java.util.ArrayList;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

import com.crashlytics.android.Crashlytics;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;

import de.rj.urwalking.config.Config;
import de.rj.urwalking.config.Constants;
import de.rj.urwalking.data.Experiment;
import de.rj.urwalking.ui.fragments.dialog.ErrorDialogFragment;

/**
 * Contains several static methods that are used in multiple locations
 * throughout the app.
 * 
 * @author rjj
 * 
 */

public class Util {

	private static final String TAG = Config.TAG;

	/**
	 * MD5-hashes a String using methods from the Apache commons codec library.
	 * 
	 * @param plaintext
	 * @return A String containing the MD5 hash
	 */
	public static String md5(String plaintext) {
		return new String(Hex.encodeHex(DigestUtils.md5(plaintext)));

	}

	/**
	 * Hides the software keyboard.
	 * 
	 * @param activity
	 */
	public static void hideSoftKeyboard(Activity activity) {
		InputMethodManager inputMethodManager = (InputMethodManager) activity
				.getSystemService(Activity.INPUT_METHOD_SERVICE);

		if (activity.getCurrentFocus() != null) {
			inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
		}
	}

	/**
	 * Register touch listeners for all Views except EditTexts in order to hide
	 * the keyboard.
	 * http://stackoverflow.com/questions/4165414/how-to-hide-soft-
	 * keyboard-on-android-after-clicking-outside-edittext
	 * 
	 * @param view
	 */

	public static void setupTouchListeners(View view, final Activity activity) {

		// Set up touch listener for non-text box views to hide keyboard.
		if (!(view instanceof EditText)) {

			view.setOnTouchListener(new OnTouchListener() {

				public boolean onTouch(View v, MotionEvent event) {
					Util.hideSoftKeyboard(activity);
					return false;
				}

			});
		}

		// If a layout container, iterate over children and seed recursion.
		if (view instanceof ViewGroup) {

			for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {

				View innerView = ((ViewGroup) view).getChildAt(i);

				setupTouchListeners(innerView, activity);
			}
		}
	}

	/**
	 * Smoothes data by applying a simple low-pass filter.
	 * http://blog.thomnichols
	 * .org/2011/08/smoothing-sensor-data-with-a-low-pass-filter
	 * http://en.wikipedia.org/wiki/Low-pass_filter#Algorithmic_implementation
	 * http://developer.android.com/reference/android/hardware/SensorEvent.html#
	 * values
	 */
	public static float[] lowPass(float[] newValues, float[] oldValues, float alpha) {

		/*
		 * time smoothing constant for low-pass filter 0 <= alpha <= 1 ; a
		 * smaller value basically means more smoothing See:
		 * http://en.wikipedia.
		 * org/wiki/Low-pass_filter#Discrete-time_realization
		 */

		if (oldValues == null) return newValues;

		for (int i = 0; i < newValues.length; i++) {
			oldValues[i] = oldValues[i] + alpha * (newValues[i] - oldValues[i]);
		}
		return oldValues;
	}

	/**
	 * Checks the network connection.
	 * 
	 * @param context
	 * @return true if there is a connection, false if not
	 */

	public static boolean hasConnection(Context context) {
		final ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();

		return activeNetwork != null && (activeNetwork.getState() == NetworkInfo.State.CONNECTED);

	}

	/**
	 * Builds a String based on an int array and a separator, much like Perl's
	 * join function.
	 * 
	 * @param glue
	 * @param ids
	 * @return
	 */
	public static String join(String glue, int[] ids) {

		if (ids.length == 0) {
			return null;
		}

		StringBuilder out = new StringBuilder();
		out.append(ids[0]);

		for (int i = 1; i < ids.length; ++i) {
			out.append(glue).append(ids[i]);
		}
		return out.toString();
	}

	/**
	 * Builds a String based on an Integer ArrayList and a separator, much like
	 * Perl's join function.
	 * 
	 * @param glue
	 * @param ids
	 * @return
	 */
	public static String join(String glue, ArrayList<Integer> ids) {

		if (ids.size() == 0) {
			return null;
		}

		StringBuilder out = new StringBuilder();
		out.append(ids.get(0));

		for (int i = 1; i < ids.size(); ++i) {
			out.append(glue).append(ids.get(i));
		}
		return out.toString();
	}

	/**
	 * Logs the maximum available memory size for this app.
	 * 
	 * @param context
	 */
	@SuppressLint("NewApi")
	public static void logAvailableMemory(Context context) {
		ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
		int memSize;
		// Large memory option is only available in Android versions >= 3.0
		// (Honeycomb)
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
			memSize = activityManager.getLargeMemoryClass();
		} else {
			memSize = activityManager.getMemoryClass();
		}
		// include memory size in next crash log
		Crashlytics.log(Log.DEBUG, Config.TAG, "Memory class: " + memSize + "MB");

	}

	public static String getAndroidVersion() {
		return Build.VERSION.RELEASE;
	}

	/**
	 * Done according to
	 * http://stackoverflow.com/questions/1995439/get-android-phone
	 * -model-programmatically
	 * 
	 * @return
	 */
	public static String getDeviceName() {
		String manufacturer = Build.MANUFACTURER;
		String model = Build.MODEL;
		if (model.startsWith(manufacturer)) {
			return capitalize(model);
		} else {
			return capitalize(manufacturer) + " " + model;
		}
	}

	/**
	 * http://ballardhack.wordpress.com/2010/09/28/subversion-revision-in-
	 * android-app-version-with-eclipse/
	 */

	public static String getUrWalkingVersion(Context context) {
		String version = "UNKNOWN";
		try {
			PackageInfo pi = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
			version = pi.versionName;
			Log.i(TAG, "URWalking version is " + pi.versionName);
		} catch (PackageManager.NameNotFoundException e) {
			Log.e(TAG, "Version name not found in package", e);
		}
		return version;

	}

	private static String capitalize(String s) {
		if (s == null || s.length() == 0) {
			return "";
		}
		char first = s.charAt(0);
		if (Character.isUpperCase(first)) {
			return s;
		} else {
			return Character.toUpperCase(first) + s.substring(1);
		}
	}

	public static final boolean deleteFile(File f) {
		boolean blnRes = false;
		try {
			Log.i(TAG, Experiment.class.getName() + ": Attempt to delete file " + f);
			boolean tmp = f.delete();
			if (tmp) {
				blnRes = true;
				Log.i(TAG, Experiment.class.getName() + ": Successfully deleted file " + f);
			} else {
				Log.i(TAG, Experiment.class.getName() + ": Failed to delete file " + f);
				blnRes = false;
			}
		} catch (Exception e) {
			Log.i(TAG, Experiment.class.getName() + "Something went wrong deleting file: " + f.getPath());
			blnRes = false;
		}
		return blnRes;
	}

	public static boolean servicesConnected(FragmentActivity activity) {
		Log.d("lego", "checking services...");
		// Check that Google Play services is available
		int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity);
		
		Log.d("lego", "resultcode = " + resultCode);
		// If Google Play services is available
		if (resultCode == ConnectionResult.SUCCESS) {
			Log.d("lego", "succes");
			// In debug mode, log the status
			if (Config.DEBUG) Log.d(Config.TAG, "Google Play services is available.");
			// Continue
			return true;
		} else {
			Log.d("lego", "fail...");
			// Google Play services was not available for some reason
			if (Config.DEBUG) Log.d(Config.TAG, "Google Play services is not available. Trying to show dialog...");
			// Get the error dialog from Google Play services
			Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(resultCode, activity,
					Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
			// If Google Play services can provide an error dialog
			
			Log.d("lego", "...");
			if (errorDialog != null) {
				Log.d("lego", "error != null");
				// Create a new DialogFragment for the error dialog
				ErrorDialogFragment errorFragment = new ErrorDialogFragment();
				// Set the dialog in the DialogFragment
				errorFragment.setDialog(errorDialog);
				// Show the error dialog in the DialogFragment
				errorFragment.show(activity.getSupportFragmentManager(), "error_dialog_fragment");
				Log.d("lego", "blub");
			}
			return false;
		}
	}
}
